/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved.
 *
 */

import assert from 'assert';
import path from 'path';

import fse from 'fs-extra';

import { matchValueWithParameterizationKey } from '../../../src/validate/dependenct-param-validate.js';
import { pathParamsOpt } from '../../../src/validate/dependency-tag-validate.js';

describe('test matchValueWithParameterizationKey', () => {
  const parameterFileJson = Object.freeze({
    version: {
      baseHar: '1.0.0',
      har2: '1.0.2',
    },
    'version.baseHar': '999.0.0',
    'version.har1': '999.0.0',

    dep: {
      har2: 'file:./packages/har2',
      outside: 'file:./libs/outside.har',
      inside: 'file:./baseHar/dir1/dir2/inside-default.tgz',
      noPrefix: './noPrefix',
      targz: './invalidFile.tar.gz',
    },

    // DTS2024121717978
    test1: {
      test2: {
        test3: {
          '@test-1_har/param.har-1_100': '2.0.0',
          '@test-1_har/param_hsp-1.10.100.hsp2.hsp2': '1.0.0',
        },
      },
    },
    'test1.test2.test3': {
      '@test-1_har/param.har-1_100': '1.0.0',
      '@test-1_har/param_hsp-1.10.100.hsp2.hsp2': '2.0.0',
    },
  });
  const pathParamsObj: Required<pathParamsOpt> = Object.freeze({
    parameterFilePath: 'D:\\MyApplication\\parameterFile.json5',
    ohPackageFilePath: 'D:\\MyApplication\\baseHar\\oh-package.json5',
  });

  // matchValueWithParameterizationKey 中使用了 fse.pathExistsSync 校验文件是否存在，需要 mock 始终返回 true
  const isDirSpy = jest.fn();
  beforeEach(() => {
    jest.spyOn(fse, 'statSync').mockReturnValue({
      isDirectory: isDirSpy,
    } as any);
    jest.spyOn(fse, 'pathExistsSync').mockReturnValue(true);
    jest.spyOn(fse.realpathSync, 'native').mockImplementation((p) => p as any);
  });
  afterEach(() => {
    isDirSpy.mockRestore();
    jest.resetAllMocks();
    jest.restoreAllMocks();
  });

  /**
   * 场景1
   * baseHar 模块的 oh-package.json5 中填写了 version: '@param:version.baseHar'
   * 需要返回 1.0.0
   */
  test('matchValueWithParameterizationKey(1)', () => {
    isDirSpy.mockReturnValue(true);
    expect(
      matchValueWithParameterizationKey({
        keys: ['version', 'baseHar'],
        parameterFileJson,
        pathParamsObj,
        dependencyName: 'version',
      }).value,
    ).toBe('999.0.0');
  });

  /**
   * 场景3
   * baseHar oh-package.json5 有 dependencies: { har2: '@param:dep.har2' }
   * parameterFile.json5 有 dep: { har2: 'file:./packages/har2' }
   * har2 oh-package.json5 有 {
   *   name: 'har2',
   *   version: '@param:version.har2',
   * }
   * 需要替换为 har2 的真实版本号 1.0.2
   */
  test('matchValueWithParameterizationKey(3)', () => {
    isDirSpy.mockReturnValue(true);
    expect(
      matchValueWithParameterizationKey({
        keys: ['dep', 'har2'],
        parameterFileJson,
        pathParamsObj,
        dependencyName: 'har2',
        ohPackageInfoGetter: () => ({
          version: '@param:version.har2',
          name: 'har2',
        }),
      }).value,
    ).toBe('1.0.2');
  });

  /**
   * 场景4
   * baseHar oh-package.json5 有 dependencies: { har1: '@param:dep.har2' }，注意这里依赖名不一致
   * parameterFile.json5 有 dep: { har2: 'file:./packages/har2' }
   * har2 oh-package.json5 有 {
   *   name: 'har2',
   *   version: '@param:version.har2',
   * }
   *
   * baseHar oh-package.json5 中写的依赖名是 har1，但查找到的模块 oh-package.json5 name 是 har2
   * 这种情况需要报错
   */
  test('matchValueWithParameterizationKey(4)', () => {
    isDirSpy.mockReturnValue(true);
    expect(() =>
      matchValueWithParameterizationKey({
        keys: ['dep', 'har2'],
        parameterFileJson,
        pathParamsObj,
        dependencyName: 'har1',
        ohPackageInfoGetter: () => ({
          version: '@param:version.har2',
          name: 'har2',
        }),
      }),
    ).toThrowError(/There are some dependency names that are inconsistent with the actual package names./);
  });

  /**
   * 场景5
   * baseHar oh-package.json5 有 dependencies: { inside: '@param:dep.inside' }
   * parameterFile.json5 有 dep: { inside: 'file:./baseHar/dir1/dir2/inside-default.tgz' }
   * 模块内部的本地产物依赖，需要保持原有的相对路径打进 baseHar.har 中
   * baseHar oh-package.json5 需要替换为相对路径 inside: 'file:./dir1/dir2/inside-default.tgz'
   * 还要额外返回 inside-default.tgz 的绝对路径，在 AbstractProcessHarArtifacts 和 PrepareSharedHarRes 中保持原有相对路径打进包中
   */
  test('matchValueWithParameterizationKey(5)', () => {
    isDirSpy.mockReturnValue(false);
    const result = matchValueWithParameterizationKey({
      keys: ['dep', 'inside'],
      parameterFileJson,
      pathParamsObj,
      dependencyName: 'inside',
      ohPackageInfoGetter: () => ({
        name: 'inside',
        version: '1.0.0',
      }),
    });
    expect(result.value).toBe('file:dir1\\dir2\\inside-default.tgz');
    expect(result.localDepArr?.[0]).toBe('D:\\MyApplication\\baseHar\\dir1\\dir2\\inside-default.tgz');
  });

  /**
   * 场景6
   * baseHar oh-package.json5 有 dependencies: { outside: '@param:dep.outside' }
   * parameterFile.json5 有 dep: { outside: 'file:./libs/outside.har' }
   * 模块外部的本地产物依赖，需要打进 baseHar.har 的根目录中
   * baseHar oh-package.json5 需要替换为相对路径 outside: 'file:outside.har'
   * 还要额外返回 outside.har 的绝对路径，在 AbstractProcessHarArtifacts 和 PrepareSharedHarRes 中拷贝到根目录
   */
  test('matchValueWithParameterizationKey(6)', () => {
    isDirSpy.mockReturnValue(false);
    const result = matchValueWithParameterizationKey({
      keys: ['dep', 'outside'],
      parameterFileJson,
      pathParamsObj,
      dependencyName: 'outside',
      ohPackageInfoGetter: () => ({
        name: 'outside',
        version: '1.0.0',
      }),
    });
    expect(result.value).toBe('file:outside.har');
    expect(result.localDepArr?.length).toBe(1);
    expect(result.localDepArr?.[0]).toBe('D:\\MyApplication\\libs\\outside.har');
  });

  /**
   * 场景7
   * 在 ohos-module-task 构造函数中调用，不传 ohPackageInfoGetter，参数化的本地依赖保持不变
   */
  test('matchValueWithParameterizationKey(7)', () => {
    const result = matchValueWithParameterizationKey({
      keys: ['dep', 'har2'],
      parameterFileJson,
      pathParamsObj,
      dependencyName: 'har2',
      ohPackageInfoGetter: undefined,
    });
    expect(result.value).toBe('@param:dep.har2');
    expect([undefined, []]).toContainEqual(result.localDepArr);
  });

  /**
   * 场景8
   * 本地依赖路径没有 file: 前缀
   */
  test('matchValueWithParameterizationKey(8)', () => {
    isDirSpy.mockReturnValue(true);
    const result = matchValueWithParameterizationKey({
      keys: ['dep', 'noPrefix'],
      parameterFileJson,
      pathParamsObj,
      dependencyName: 'noPrefix',
      ohPackageInfoGetter: () => ({
        name: 'noPrefix',
        version: '1.0.17',
      }),
    });
    expect(result.value).toBe('1.0.17');
    expect([undefined, []]).toContainEqual(result.localDepArr);
  });

  /**
   * 场景9
   * 本地产物依赖文件格式不正确
   */
  test('matchValueWithParameterizationKey(9)', () => {
    isDirSpy.mockReturnValue(false);
    expect(() =>
      matchValueWithParameterizationKey({
        keys: ['dep', 'targz'],
        parameterFileJson,
        pathParamsObj,
        dependencyName: 'targz',
        ohPackageInfoGetter: () => ({
          name: 'targz',
          version: 'targz',
        }),
      }),
    ).toThrowError(/Unrecognized archive format in parameterFile./);
  });

  test('DTS2024121717978', () => {
    const result = matchValueWithParameterizationKey({
      keys: ['test1', 'test2', 'test3', '@test-1_har/param.har-1_100'],
      parameterFileJson,
      pathParamsObj,
      dependencyName: 'version',
    });
    expect(result.value).toBe('1.0.0');
  });

  test('DTS2024121717978', () => {
    isDirSpy.mockReturnValue(true);
    const parameterFileJson = {
      'a.b': {
        c: 'file:./ab_c',
      },
      a: {
        'b.c': 'file:./a_bc',
      },
    };
    const keys = 'a.b.c'.split('.');
    const result = matchValueWithParameterizationKey({
      keys,
      parameterFileJson,
      pathParamsObj,
      dependencyName: 'dep',
      ohPackageInfoGetter: (depOhPackageJsonPath) => {
        // 应该是从 parameterFileJson 中匹配到 ab_c，而不是 a_bc
        assert.equal(path.normalize(depOhPackageJsonPath), path.normalize('D:/MyApplication/ab_c/oh-package.json5'));
        return {
          name: 'dep',
          version: '1.2.3',
        };
      },
    });
    expect(result.value).toBe('1.2.3');
  });

  test('DTS2024121717978', () => {
    isDirSpy.mockReturnValue(true);
    const parameterFileJson = {
      'a.b.c': {
        'd.e': {
          f: 'file:./abc_d_e',
        },
      },
      a: {
        'b.c.d.e': 'file:./a_bcde',
      },
    };
    const keys = 'a.b.c.d.e'.split('.');
    const result = matchValueWithParameterizationKey({
      keys,
      parameterFileJson,
      pathParamsObj,
      dependencyName: 'dep',
      ohPackageInfoGetter: (depOhPackageJsonPath) => {
        // 应该是从 parameterFileJson 中匹配到 a_bcde，而不是 abc_d_e
        assert.equal(path.normalize(depOhPackageJsonPath), path.normalize('D:/MyApplication/a_bcde/oh-package.json5'));
        return {
          name: 'dep',
          version: '1.2.3',
        };
      },
    });
    expect(result.value).toBe('1.2.3');
  });

  test('DTS2024121717978', () => {
    isDirSpy.mockReturnValue(true);
    const parameterFileJson = {
      'a.b.c': {
        d: 'file:./abc_d',
      },
    };
    const keys = 'a.b.c'.split('.');
    expect(() =>
      matchValueWithParameterizationKey({
        keys,
        parameterFileJson,
        pathParamsObj,
      }),
    ).toThrowError(/the value of a.b.c in parameterFile./);
  });
});
